//
//  ErrorHandler.swift
//  Infrastructure
//
//  Created by hanlei on 2022/3/4.
//

import Foundation
import RxSwift
import RxCocoa

public final class ErrorHandler: SharedSequenceConvertibleType {
  public typealias SharingStrategy = DriverSharingStrategy
  public typealias Element = Error
  
  private let subject = PublishSubject<Error>()
  
  public init() {}
  
  func handle<S: ObservableConvertibleType>(from source: S) -> Observable<S.Element> {
    source.asObservable()
      .materialize()
      .flatMap { event -> Observable<S.Element> in
        switch event {
        case .next(let element):
          return Observable<S.Element>.just(element)
        case .error(let error):
          self.subject.onNext(error)
          return Observable<S.Element>.empty()
        case .completed:
          return Observable<S.Element>.empty()
        }
      }
  }
  
  func `continue`<S: ObservableConvertibleType>(from source: S) -> Observable<S.Element> {
    source.asObservable()
      .materialize()
      .flatMap { event -> Observable<S.Element> in
        switch event {
        case .next(let element):
          return Observable<S.Element>.just(element)
        case .error(let error):
          self.subject.onNext(error)
          return Observable<S.Element>.never()
        case .completed:
          return Observable<S.Element>.never()
        }
      }
  }
  
  public func asSharedSequence() -> SharedSequence<SharingStrategy, Error> {
    subject.asDriver(onErrorRecover: { Driver<Error>.of($0) })
  }
}

extension ErrorHandler {
  public func accept(_ error: Error) {
    subject.onNext(error)
  }
}

extension ObservableConvertibleType {
  public func asObservable(continue errorHandler: ErrorHandler) -> Observable<Element> {
    return errorHandler.continue(from: self)
      .observe(on: DriverSharingStrategy.scheduler)
  }
  
  public func asObservable(drainageError errorHandler: ErrorHandler) -> Observable<Element> {
    return errorHandler.handle(from: self)
      .observe(on: DriverSharingStrategy.scheduler)
  }
}
